package cn.script.fxj.bind;

import cn.script.fxj.bind.utils.BindRectField;
import cn.script.fxj.bind.utils.BindStringUtils;
import javafx.application.Platform;
import javafx.beans.property.Property;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 创建fx 数据绑定bean
 */
public class FxBeanFactory {

    /**
     * 创建对象
     *
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> tClass, BindOpt bindOpt) {
        return new FxBeanInterceptor().instance(tClass, bindOpt);
    }

    static class FxBeanInterceptor implements MethodInterceptor {


        private Object tObj;
        private Class tClass;
        Map<String, Property> allMap = new HashMap<String, Property>();

        public <T> T instance(Class<T> tClass, BindOpt bindOpt) {
            this.tClass = tClass;
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(tClass);
            // 回调方法
            enhancer.setCallback(this);
            // 创建代理对象
            this.tObj = enhancer.create();
            try {
                bindOpt.bind(this.allMap, tClass, this.tObj);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return (T) this.tObj;
        }


        private void setHandler(Object obj, Method method,
                                Object[] args, MethodProxy proxy, String fieldName) {
            Object val = args[0];
            Property property = this.allMap.get(fieldName);
            Platform.runLater(() -> {
                try {
                    property.setValue(val);
                } catch (Exception e) {
                    e.printStackTrace();
                }

            });

        }

        private Object getHandler(Object obj, Method method,
                                  Object[] args, MethodProxy proxy, String fieldName) throws Throwable {
            Property property = this.allMap.get(fieldName);
            Object pval = property.getValue();
            Object val = proxy.invokeSuper(obj, args);
            if (val == pval || (val != null && val.equals(pval))) {
                return val;
            }
            BindRectField.setFieldValue(obj, this.tClass.getDeclaredField(fieldName), pval);
            return pval;
        }

        // 回调方法
        @Override
        public Object intercept(Object obj, Method method, Object[] args,
                                MethodProxy proxy) throws Throwable {

            String name = method.getName();
            String fieldName = BindStringUtils.lowerFirstCase(name.substring(3, name.length()));
            if (this.allMap.get(fieldName) == null) {
                return proxy.invokeSuper(obj, args);
            }

            if (name.startsWith("set")) {
                this.setHandler(obj, method, args, proxy, fieldName);
                return proxy.invokeSuper(obj, args);
            }
            if (name.startsWith("get")) {
                return this.getHandler(obj, method, args, proxy, fieldName);
            }
            return proxy.invokeSuper(obj, args);
        }
    }
}
